﻿using System.Collections.Generic;
using System.ComponentModel.Composition;
using Mono.Cecil;
using WeavingCommon;

namespace NotifyPropertyWeaverMsBuildTask
{
    [Export, PartCreationPolicy(CreationPolicy.Shared)]
    public class ShouldNotifyForAllWalker
    {
        WeavingTask weavingTask;
        TypeNodeBuilder typeNodeBuilder;
        TypeResolver typeResolver;

        [ImportingConstructor]
        public ShouldNotifyForAllWalker(WeavingTask weavingTask, TypeNodeBuilder typeNodeBuilder, TypeResolver typeResolver)
        {
            this.weavingTask = weavingTask;
            this.typeNodeBuilder = typeNodeBuilder;
            this.typeResolver = typeResolver;
        }

        void Process(List<TypeNode> notifyNodes, bool parentContainsNotifyForAllAttribute)
        {
            foreach (var notifyNode in notifyNodes)
            {
                var shouldNotifyForAllInType = false;
                if (weavingTask.TryToWeaveAllTypes)
                {
                    shouldNotifyForAllInType = true;
                }
                else if (parentContainsNotifyForAllAttribute)
                {
                    shouldNotifyForAllInType = true;
                }
                else if (notifyNode.TypeDefinition.CustomAttributes.ContainsAttribute("NotifyForAllAttribute"))
                {
                    shouldNotifyForAllInType = true;
                }
                notifyNode.ShouldNotifyForAllInType = shouldNotifyForAllInType;
                Process(notifyNode.Nodes, shouldNotifyForAllInType);
            }
        }

        public bool DoesHierachyContainNotifyForAllAttribute(TypeDefinition type)
        {
            var containsNotifyForAll = type.CustomAttributes.ContainsAttribute("NotifyForAllAttribute");
            if (containsNotifyForAll)
            {
                return true;
            }
            if (type.BaseType != null)
            {
                if (type.BaseType.Name != "Object")
                {
                    var typeDefinition = typeResolver.Resolve(type.BaseType);
                    return DoesHierachyContainNotifyForAllAttribute(typeDefinition);
                }
            }
            return false;
        }

        public void Execute()
        {
            Process(typeNodeBuilder.NotifyNodes, false);
        }
    }
}